package com.ak.game.netty;

import com.ak.game.entity.vo.BetReqVo;
import com.ak.game.service.BettingService;
import com.ak.game.service.RemoteWebSocketClientService;
import com.ak.game.util.AjaxResult;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Slf4j
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private final String gameId;

    private final RemoteWebSocketClientService clientService;

    private final BettingService bettingService;

    private ChannelHandlerContext ctx;

    public WebSocketFrameHandler(String gameId, RemoteWebSocketClientService clientService, BettingService bettingService) {
        this.bettingService = bettingService;
        this.gameId = gameId;
        this.clientService = clientService;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        this.ctx = ctx;
        super.channelActive(ctx);
    }

    /**
     * 重写SimpleChannelInboundHandler的channelRead0方法，处理接收到的消息
     *
     * @param ctx the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
     *            belongs to
     * @param msg the message to handle
     */
    @Override
    public void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        String text = msg.text();
        JSONObject jsonObject = JSONObject.parseObject(text);
        String msgId = jsonObject.getString("msg_id");
        Map<String, List<String>> params = (Map<String, List<String>>) ctx.channel().attr(AttributeKey.valueOf("params")).get();
        String userId = params.get("user_id").get(0);
        switch (msgId) {
            // 下注请求
            case "1004":
                log.info("收到msg_id=1004下注请求，请求参数={}",text);
                String timestamp = String.valueOf(System.currentTimeMillis());
                BetReqVo betReqVo = new BetReqVo();
                Map<String, Object> data = new HashMap<>();
                data.put("user_id", userId);
                data.put("type", jsonObject.get("type"));
                data.put("amount", jsonObject.get("amount"));
                data.put("multiple", 1);
                betReqVo.setData(data);
                Map<String, Object> game = new HashMap<>();
                game.put("id", gameId);
                game.put("round_id", timestamp);
                game.put("start_time", timestamp);
                betReqVo.setGame(game);
                CompletableFuture<Object> objectCompletableFuture = clientService.sendMessage(JSON.toJSONString(betReqVo));
                try {
                    Object result = objectCompletableFuture.get(10, TimeUnit.SECONDS);
                    AjaxResult ajaxResult = (AjaxResult) result;
                    // 如果接口api返回的code=200，则走游戏业务逻辑
                    if (ajaxResult.getCode() == 200) {
                        Map<String, Object> bet = bettingService.bet(jsonObject,userId);
                        bet.put("user_id", userId);
                        bet.put("type", jsonObject.get("type"));
                        bet.put("amount", jsonObject.get("amount"));
                        bet.put("round_id", timestamp);
                        bet.put("start_time", timestamp);
                        // 返回数据给前端
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(AjaxResult.success(bet,"1004"))));
                        for (Map.Entry<String, ChannelHandlerContext> entry : BettingService.USER_CTX_MAP.entrySet()) {
                            entry.getValue().channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(AjaxResult.success(bet,"1005"))));
                        }
                        // 返回数据给接口api端
                        BetReqVo betReqVoResult = new BetReqVo();
                        Map<String, Object> dataResult = Map.of("user_id", userId, "amount", jsonObject.get("amount"), "multiple", bet.get("multiple"), "type", jsonObject.get("type"), "win", bet.get("win"), "result", bet.get("result"));
                        betReqVoResult.setData(dataResult);
                        Map<String, Object> gameResult = Map.of("id", gameId, "round_id", timestamp, "start_time", timestamp, "end_time", System.currentTimeMillis());
                        betReqVoResult.setGame(gameResult);
                        betReqVoResult.setMsg_id("105");
                        CompletableFuture<Object> objectCompletableFuture1 = clientService.sendMessage(JSON.toJSONString(betReqVoResult));
                        Boolean response = true;
                        do {
                            response = (Boolean) objectCompletableFuture1.get(10, TimeUnit.SECONDS);
                        } while (!response);
                    } else {
                        // 返回数据给前端
                        ajaxResult.setMsg_id("1004");
                        ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(ajaxResult)));
                    }

                } catch (InterruptedException | ExecutionException | TimeoutException e) {
                    throw new RuntimeException(e);
                }
                break;
        }
    }

    /**
     * 管道异常处理，防止资源泄露
     *
     * @param ctx   通道代理上下文
     * @param cause 异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        String userId = ctx.channel().attr(AttributeKey.valueOf("userId")).get().toString();
        RemoteWebSocketClientService.FUTURES.remove(userId);
        BettingService.USER_CTX_MAP.remove(userId);
        BettingService.USER_IS_TRY_OUT.remove(userId);
        log.error("连接失败：{}", cause.getMessage()); // 记录日志
        ctx.close(); // 关闭发生异常的通道，防止资源泄露
    }


}
